/* Copyright (c) 2009 clueless
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdio.h>
#include <windows.h>
#include <stdint.h>

// FIXME: It's possible to cause a crash by placing the hooks while
//        an instruction is being executed in another thread
//        probility is fairly low

#ifndef LOGFILE
# warning "The default log file is most likely not gonna work on your system"
# warning "pass a LOGFILE define to the compiler or edit the source file"
# define LOGFILE "Z:\\rom.log"
#endif

#define SEND_FUNCTION 0x006DC530
#define SEND_ORIGINAL "\x8B\x44\x24\x04\x83"
#define RECV_FUNCTION 0x006DBD44
#define RECV_ORIGINAL "\x8B\x44\x24\x28\x0F"
extern void *SendHookStub;
extern void *RecvHookStub;

FILE *logFile;

uintptr_t originalSendFunction = SEND_FUNCTION + 7; // First 7 bytes are replaced in the stub
uintptr_t originalRecvFunction = RECV_FUNCTION + 8; // First 7 bytes are replaced in the stub

void dump(FILE *outstream, uint8_t *buffer, uint32_t len)
{
    char hex[256] = {0};
    char plain[256] = {0};

    for(uint32_t i = 0; i < len; i++)
    {
        if(i % 16 == 0)
            sprintf(hex, "%08x    ", i); // Offset indicator

        sprintf(hex, "%s%02x ", hex, buffer[i]); // hexbytes
        
        //plainbytes
        if(buffer[i] >= ' ' && buffer[i] <= '~')
            sprintf(plain, "%s%c", plain, buffer[i]);
        else
            sprintf(plain, "%s.", plain);

        // Spacefill
        if(i == (len - 1))
        {
            for(uint32_t j = 0; (i + j + 1) % 16 != 0; j++)
                sprintf(hex, "%s   ", hex);
        }
        
        // Line
        if((i + 1) % 16 == 0 || i == (len - 1))
        { 
            fprintf(outstream, "%s      %s\n", hex, plain);
            hex[0] = '\0';
            plain[0] = '\0';
        }
    }
    fprintf(outstream, "\n");
    fflush(outstream);
}

void __stdcall
SendHook(uint8_t *buffer, uint32_t size)
{
    fprintf(logFile, "SendHook(0x%08x, 0x%08x [%u]);\n", buffer, size, size);
    dump(logFile, buffer, size);
}

void __stdcall
RecvHook(uint8_t *buffer, uint32_t size)
{
    fprintf(logFile, "RecvHook(0x%08x, 0x%08x [%u]);\n", buffer, size, size);
    dump(logFile, buffer, size);
}

void InstallHooks(void)
{
    uint8_t *jmpbyte;
    uintptr_t *jmpaddr;
    DWORD oldProtect;
   
    logFile = fopen(LOGFILE, "at");
    
    if(logFile == NULL)
        MessageBox(NULL, "log file returned NULL, crash imminent.", NULL, 0);

    // Drop send hook
    jmpbyte = (void *)SEND_FUNCTION;
    jmpaddr = (void *)(SEND_FUNCTION + 1);
    VirtualProtect((void *)SEND_FUNCTION, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    *jmpbyte = '\xE9';
    *jmpaddr = (uintptr_t)(&SendHookStub) - (SEND_FUNCTION + 5);
    VirtualProtect((void *)SEND_FUNCTION, 5, oldProtect, &oldProtect); 
    
    // Drop recv hook
    jmpbyte = (void *)RECV_FUNCTION;
    jmpaddr = (void *)(RECV_FUNCTION + 1);
    VirtualProtect((void *)RECV_FUNCTION, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    *jmpbyte = '\xE9';
    *jmpaddr = (uintptr_t)(&RecvHookStub) - (RECV_FUNCTION + 5);
    VirtualProtect((void *)RECV_FUNCTION, 5, oldProtect, &oldProtect); 
}

void RemoveHooks(void)
{
    DWORD oldProtect;
    
    // Drop send hook
    VirtualProtect((void *)SEND_FUNCTION, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy((void *)SEND_FUNCTION, SEND_ORIGINAL, 5);
    VirtualProtect((void *)SEND_FUNCTION, 5, oldProtect, &oldProtect); 
    
    // Drop recv hook
    VirtualProtect((void *)RECV_FUNCTION, 5, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy((void *)RECV_FUNCTION, RECV_ORIGINAL, 5);
    VirtualProtect((void *)RECV_FUNCTION, 5, oldProtect, &oldProtect); 
    
    fclose(logFile);
}

int __stdcall
DllMain(HINSTANCE hinstDLL __attribute__((unused)),
        DWORD fdwReason,
        LPVOID lpvReserved __attribute__((unused)))
{
    switch(fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            InstallHooks();
            break;
        case DLL_PROCESS_DETACH:
            RemoveHooks();
            break;
    }
    return TRUE;
}
